package com.wyw.learning.thread.flowcontrol.semaphore;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 演示Semaphore
 *  Semaphore可以用来限制或管理数量有限的资源的使用情况
 *  "污染不能太多，污染证只能发3张"
 *  信号量的作用是维护一个"许可证"的技术，线程可以"获取"许可证，拿信号量剩余的许可证就减一，线程也可以"释放"一个许可证，那信号量剩余的许可证就加一，当信号量所拥有的许可证为0，那么下一个还想要获取许可证的线程，就需要等待，直到有其他线程释放了许可证
 *
 * 信号量的使用流程
 *  初始化Semaphore并指定许可证的数量
 *  在需要被现在的代码前加acquire()或者acquireUninterruptibly()方法
 *  在任务执行结束后，调用release()来释放许可证
 *
 * 信号量主要方法介绍
 *  newSemaphore(int permits, boolean fair):这里可以设置是否要使用公平策略，如果传入true，那么Semaphore会把之前等待的线程放到FIFO的队列里，以便于当有了新的许可证，可以分发给之前等待时间最长的线程
 *      acquire()
 *      acquireUninterruptibly()
 *  tryAcquire()：看看现在有没有空闲的许可证，如果有的话就去获取，没有也没关系，不必陷入阻塞，可以先去处理其他逻辑，过段时间再来查看是否有许可证空闲
 *  tryAcquire(timeout)：和tryAcquire()一样，但是多了一个超时时间，在规定时间内获得不了许可证，就去做其他任务
 *      获得许可证了一定要调用release()释放许可证
 *
 * 信号量特殊用法
 *  一次性获取或者释放多个许可证
 *      比如TaskA会调用很消耗资源的method1()，而TaskB调用不是很消耗资源的method2()
 *      计入一共有五个许可证，那么就可以要求TaskA获取五个许可证才能执行，而TaskB需要一个许可证就可执行，这样就避免了A和B同时运行的情况，我们可以根据自己的需求合理分配资源
 *
 * 注意点
 *  获取和释放的许可证数量必须一直，否则比如每次都获取2个但是只需要释放1个设置不释放，随着时间的推移，到最后许可证数量不够用，会导致程序卡死（虽然信号量类并不对是否和获取的数量做规定，但是这是我们的编程规范，否则容易出错）
 *  注意在初始化Semaphore的时候设置公平性，一般设置为true比较合理
 *  并不是必须由获取许可证的线程释放那个许可证，事实上，获取和释放对象成并无要求，也许是A获取了，然后由B释放，只要逻辑合理即可
 *  信号量的作用，除了控制临界区最多同时有N个线程访问外，另一个作用是可以实现"条件等待"，例如线程1需要在线程2完成准备工作后才能开始工作，那么线程1 acquire()，而线程2完成任务后release()，这样的话，相当于是轻量级的CountDownLatch（Semaphore如果只设置许可证为1，那么可以作为单线程的门闩。CountDownLatch可以作为多个线程的门闩）
 *
 *
 * @author Mr Wu    yewen.wu.china@gmail.com
 * <p>
 * Update History:
 * Author        Time            Content
 */
public class SemaphoreDemo {
    static Semaphore semaphore =
            new Semaphore(5, true);//注意在初始化Semaphore的时候设置公平性，一般设置为true会更合理

    public static void main(String[] args) {
        ExecutorService service =
                Executors.newFixedThreadPool(50);
        for (int i = 0; i < 100; ) {
            service.submit(new Task());
        }
        service.shutdown();
    }

    static class Task implements Runnable {

//        @SneakyThrows
        @Override
        public void run() {
            try {
                semaphore.acquire(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "拿到了许可证");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "释放了许可证");
            semaphore.release(3);
//            semaphore.release(2);//拿到多少释放多少，如果释放少了，会出现再也拿不到的情况，会导致程序卡死，不是强制，是规范

        }
    }
}
